/*
 * uthreads.cpp
 *
 *  Created on: Apr 2, 2012
 *      Author: ariel
 */


#include <cstdlib>
#include <signal.h>
#include <iostream>

#include "uthreads.h"
#include "scheduler.h"

using namespace std;

#define SUCCESS_EXIT 0
#define FAIL_EXIT -1

/*
 * this class mask the virtual timer signal.
 * In each external method of the scheduler there should a local instance of this
 * class in order to block any setups of the virtual timer.
 */
class Virtual_Timer_Controler
{
public:
	Virtual_Timer_Controler()
	{
		sigemptyset( & _set );

		sigaddset( & _set, SIGVTALRM );

		sigprocmask( SIG_BLOCK, & _set, NULL );
	}

	~Virtual_Timer_Controler()
	{
		sigprocmask( SIG_UNBLOCK, & _set, NULL );
	}

private:
	sigset_t _set;

};

// the scheduler instance
static Scheduler *scheduler;

// handler for virtual timer signal
void timer_handler(int signum)
{
	scheduler->vtimer_handler();
}

/* Initialize thread library */
int thread_lib_init()
{
	scheduler = new Scheduler();

	scheduler->keep_main_thread_u_area();

	// assign handler for the virtual timer
	struct sigaction new_action;

	new_action.sa_handler = timer_handler;

	new_action.sa_flags = 0;

	if ( sigemptyset( & new_action.sa_mask )
			|| sigaction( SIGVTALRM, & new_action, NULL) )
	{
		cerr << "system error: couldn't set timer handler" << endl;

		delete scheduler;

		return FAIL_EXIT;
	}

	return SUCCESS_EXIT;
}

/* Create a new thread whose entry point is f */
int thread_spawn(void (*thread_func)(void), int priority)
{
	result_t result;
	int id;
	Virtual_Timer_Controler timer;

	if ( priority < 0 || priority > MAX_PRI )
	{
		cerr << "thread library error: invalid priority" << endl;
		return FAIL_EXIT;
	}

	if ( NULL == thread_func )
	{
		// null pointer as entry point was given
		cerr << "thread library error: null pointer as entry point was given" << endl;
		return FAIL_EXIT;
	}

	result = scheduler->thread_create( thread_func, priority, & id );

	if ( ERROR_SYSTEM_CALL == result )
	{
		thread_terminate( scheduler->get_running_thread_id() );

		// shouldn't be here
		return FAIL_EXIT;
	}

	if ( OPERATION_SUCCESS != result )
	{
		cerr << "thread library error: creation of a new thread failed" << endl;
	}

	return (OPERATION_SUCCESS == result ) ? id :  FAIL_EXIT;
}

/* Terminate a thread */
int thread_terminate(int tid)
{
	result_t result;
	Virtual_Timer_Controler timer;

	if ( tid < 0 )
	{
		cerr << "thread library error: invalid thread id" << endl;
		return FAIL_EXIT;
	}

	if ( 0 == tid )
	{
		// terminate the main thread
		delete scheduler;
		exit(SUCCESS_EXIT);
	}

	result = scheduler->thread_terminate(tid);

	switch ( result ) {
	case ERROR_SYSTEM_CALL:

		// make sure we don't have recursive termination
		if ( scheduler->get_running_thread_id() != (uint)tid )
		{
			thread_terminate( scheduler->get_running_thread_id() );
		}
		return FAIL_EXIT;

	case ERROR_THREAD_UNKNOWN:

		cerr << "thread library error: unknown thread id was given" << endl;

		break;

	case OPERATION_SUCCESS:
		break;

	default:

		cerr << "thread library error: thread termination failed" << endl;
		break;

	}

	return ( OPERATION_SUCCESS == result ) ? SUCCESS_EXIT : FAIL_EXIT;

}

/* Sleep */
int thread_sleep(int tid, int num_millisecs)
{
	result_t result;
	Virtual_Timer_Controler timer;

	if ( tid < 0 || num_millisecs <= 0 )
	{
		cerr << "thread library error: invalid arguments" << endl;
		return FAIL_EXIT;
	}

	if ( 0 == tid )
	{
		cerr << "thread library error: main thread can't sleep" << endl;
		return FAIL_EXIT;
	}

	result = scheduler->thread_sleep( tid, num_millisecs );

	switch ( result ) {

	case ERROR_SYSTEM_CALL:

		thread_terminate( scheduler->get_running_thread_id() );
		return FAIL_EXIT;

	case ERROR_THREAD_UNKNOWN:

		cerr << "thread library error: unknown thread id was given" << endl;
		break;

	case ERROR_THREAD_ALREADY_SLEEPING:

		cerr << "thread library error: there is a sleeping thread already" << endl;
		break;

	case OPERATION_SUCCESS:
		break;

	default:

		cerr << "thread library error: thread sleep failed" << endl;
		break;
	}

	return ( OPERATION_SUCCESS == result ) ? SUCCESS_EXIT : FAIL_EXIT;
}

/* Suspend */
int thread_suspend(int tid)
{
	result_t result;
	Virtual_Timer_Controler timer;

	if ( tid < 0 )
	{
		cerr << "thread library error: invalid thread id" << endl;
		return FAIL_EXIT;
	}

	if ( 0 == tid )
	{
		cerr << "thread library error: main thread can't be suspended" << endl;
		return FAIL_EXIT;
	}

	result = scheduler->thread_suspend(tid);

	switch ( result ) {

	case ERROR_SYSTEM_CALL:

		thread_terminate( scheduler->get_running_thread_id() );
		return FAIL_EXIT;

	case ERROR_THREAD_UNKNOWN:

		cerr << "thread library error: unknown thread id was given" << endl;
		break;

	case OPERATION_SUCCESS:
		break;

	default:

		cerr << "thread library error: thread suspend failed" << endl;
		break;
	}

	return ( OPERATION_SUCCESS == result ) ? SUCCESS_EXIT : FAIL_EXIT;
}

/* Resume */
int thread_resume(int tid)
{
	result_t result;
	Virtual_Timer_Controler timer;

	if ( tid < 0 )
	{
		cerr << "thread library error: invalid thread id" << endl;
		return FAIL_EXIT;
	}

	result = scheduler->thread_resume( tid );


	switch ( result ) {

	case ERROR_SYSTEM_CALL:

		thread_terminate( scheduler->get_running_thread_id() );
		return FAIL_EXIT;

	case ERROR_THREAD_UNKNOWN:

		cerr << "thread library error: unknown thread id was given" << endl;
		break;

	case OPERATION_SUCCESS:
		break;

	default:

		cerr << "thread library error: thread resume failed" << endl;
		break;
	}


	return ( OPERATION_SUCCESS == result ) ? SUCCESS_EXIT : FAIL_EXIT;
}

/* Set Priority */
int thread_set_priority(int tid, int priority)
{
	result_t result;
	Virtual_Timer_Controler timer;


	if ( priority < 0 || priority > MAX_PRI )
	{
		cerr << "thread library error: invalid thread priority" << endl;
		return FAIL_EXIT;
	}

	if ( tid < 0 )
	{
		cerr << "thread library error: invalid thread id" << endl;
		return FAIL_EXIT;
	}

	result = scheduler->set_thread_priority( tid, priority );


	switch ( result ) {

	case ERROR_SYSTEM_CALL:

		thread_terminate( scheduler->get_running_thread_id() );
		return FAIL_EXIT;

	case ERROR_THREAD_UNKNOWN:

		cerr << "thread library error: unknown thread id was given" << endl;
		break;

	case OPERATION_SUCCESS:
		break;

	default:

		cerr << "thread library error: setting thread's priority failed" << endl;
		break;
	}

	return ( OPERATION_SUCCESS == result ) ? SUCCESS_EXIT : FAIL_EXIT;
}

/* Get thread id */
int thread_gettid()
{
	Virtual_Timer_Controler timer;
	return (int)( scheduler->get_running_thread_id() );
}






